package im.yisme.model;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

import android.util.Log;
import android.view.MotionEvent;
import im.yisme.R.id;
import im.yisme.model.fileio.LyricsReader;
import im.yisme.model.game.AccuracyAssessingSystem;
import im.yisme.model.game.PackagedGameAction;
import im.yisme.model.game.ScoringSystem;
import im.yisme.model.service.GameSystemService;
import im.yisme.vo.InputResultVO;
import im.yisme.vo.LyricsActionVO;
import im.yisme.vo.LyricsVO;
import im.yisme.vo.MusicVO;
import im.yisme.vo.RawActionVO;

public class GameSystem implements GameSystemService{

	private static GameSystem gameSystem;
	private MusicVO playingMusic;
	private LyricsVO playingLyrics;
	private boolean ready;
	private boolean running;
	
	private ArrayList<LyricsActionVO> lyricsList;
	private ArrayList<LyricsActionVO> currentLyricsList;
	private int playIndex,judgeIndex;
	
	//计分系统
	private ScoringSystem scoringSystem;
	//判断准确度系统
	private AccuracyAssessingSystem accuracyAssessingSystem;
	//动作包装队列
	private ArrayList<PackagedGameAction> pActionList;
	private boolean reverse,twoPointer;
	
	private GameSystem(){
		init();
	}
	
	public InputResultVO handleInput(RawActionVO action) {
		// TODO Auto-generated method stub
		InputResultVO resultVO = new InputResultVO(action.getAction(), false);
		
		
		switch (action.getAction()) {
		case RawActionVO.DOWN:
			
			PackagedGameAction newAction = new PackagedGameAction(action.getMoment());
			newAction.addAction(action.getAction(), action.x1, action.y1);
			
			pActionList.add(newAction);
			resultVO = handlePackagedAction(newAction);
			
			break;
		case RawActionVO.MOVE:
			
			if(pActionList.size() == 1){
				PackagedGameAction oriAction = pActionList.get(0);
				oriAction.addAction(action.getAction(), action.x1, action.y1);
			}
			else {
				PackagedGameAction oriAction1 = pActionList.get(0);
				PackagedGameAction oriAction2 = pActionList.get(1);
				
				if(twoPointer){
					if(reverse){
						oriAction1.addAction(action.getAction(), action.x2, action.y2);
						oriAction2.addAction(action.getAction(), action.x1, action.y1);

					}
					else {
						oriAction1.addAction(action.getAction(), action.x1, action.y1);
						oriAction2.addAction(action.getAction(), action.x2, action.y2);
					}
				}
				else {
	
					float distance11 = oriAction1.getDistance(action.x1, action.y1);
					float distance21 = oriAction1.getDistance(action.x2, action.y2);
					
					if(distance11 < distance21){
						oriAction1.addAction(action.getAction(), action.x1, action.y1);
						reverse = false;
					}
					else {
						oriAction2.addAction(action.getAction(), action.x2, action.y2);
						reverse = true;
					}
					
					twoPointer = true;
				}
			}
			break;
		case RawActionVO.UP:
			twoPointer = false;
			
			if(pActionList.size() == 1){
				PackagedGameAction oriAction = pActionList.get(0);
				oriAction.addAction(action.getAction(), action.x1, action.y1);
				
				resultVO = handlePackagedAction(oriAction);
				
				pActionList.remove(oriAction);
			}
			else {
				PackagedGameAction oriAction1 = pActionList.get(0);
				PackagedGameAction oriAction2 = pActionList.get(1);
				
				float distance11 = oriAction1.getDistance(action.x1, action.y1);
				float distance12 = oriAction2.getDistance(action.x1, action.y1);
				
				if(distance11 < distance12){
					oriAction1.addAction(action.getAction(), action.x1, action.y1);
					resultVO = handlePackagedAction(oriAction1);
					
					pActionList.remove(oriAction1);
				}
				else {
					oriAction2.addAction(action.getAction(), action.x1, action.y1);
					resultVO = handlePackagedAction(oriAction2);
					
					pActionList.remove(oriAction2);
				}
			}
			
			break;
		default:
			break;
		}
		
		return resultVO;
	}	
	

	private InputResultVO handlePackagedAction(PackagedGameAction action){
		InputResultVO resultVO=new InputResultVO(action.getAction(), false);

		if(currentLyricsList.size() == 0) currentLyricsList = getActiveLyrics();
		
		if(currentLyricsList.size() == 0) return resultVO;
		
		LyricsActionVO currentAction = currentLyricsList.get(0);
		
		
		
		if(currentAction.getType() == LyricsAction.TAP){
			LyricsActionVO nextAction = null;
			if(currentLyricsList.size() > 1) nextAction = currentLyricsList.get(1);
			
			switch (action.getAction()) {
			case RawActionVO.DOWN:
				if( action.getStartSection() == currentAction.getPoint()){
					validSection(action.getMoment(), currentAction, resultVO);
				}
				else {
					if(nextAction != null && action.getStartSection() == nextAction.getPoint()){
						validSection(action.getMoment(), nextAction, resultVO);
					}
					else {
						scoringSystem.InvalidAction();
					}
				}
				break;
			default:
				break;
			}
		}
		else {
			LyricsActionVO currentActionTail = null;
			LyricsActionVO nextDragAction = null;
			LyricsActionVO nextDragActionTail = null;
			
			currentActionTail = currentLyricsList.get(1);
			if(currentLyricsList.size() > 2){
				nextDragAction = currentLyricsList.get(2);
				nextDragActionTail = currentLyricsList.get(3);
			}
			Log.i("GameSystem", "drag");
			switch (action.getAction()) {
			
			case RawActionVO.DOWN:
				
				break;
			case RawActionVO.UP:
				if(currentAction.getConnectionType() == LyricsAction.BEGIN_POINT){
					int end = action.getEndSection();
					int start = action.getStartSection();
					
					Log.i("GameSystem", start+":"+end);
					if(currentAction.getPoint() == start && currentActionTail.getPoint() == end){
						Log.i("GameSystem", "begin_point_valid");
						validFling(action.getStartMoment(), action.getMoment(), currentAction, currentActionTail, resultVO);
					}
					else {
						Log.i("GameSystem", "next_point_valid");
						if(nextDragAction != null && nextDragAction.getPoint() == start && nextDragActionTail.getPoint() == end){
							validFling(action.getStartMoment(), action.getMoment(), nextDragAction, nextDragActionTail, resultVO);
						}
						else {
							scoringSystem.InvalidAction();
						}					
					}
				}
				break;
				
			default:
				break;
			}
		}
		
        return resultVO;
	}
	
	private void validSection(int hitTime,LyricsActionVO currentAction, InputResultVO result){
		float accuracy=accuracyAssessingSystem.getAccuracy(hitTime, currentAction.getTime());
		 Log.i("GameSystem", "accuracy:"+accuracy);  
		   if(accuracy == -1){
			   scoringSystem.InvalidAction();
		   }
		   else {
			   scoringSystem.addValidHit(accuracy);
			   currentLyricsList.remove(currentAction);
			   result.setSuccess(true);
			   result.setLevel(getLevel(accuracy));
			   result.setWhichPillar(currentAction.getPoint());
		   }
	}
	private void validFling(int startHitTime,int endHitTime, LyricsActionVO head, LyricsActionVO tail, InputResultVO result){
		float accuracyHead = accuracyAssessingSystem.getAccuracy(startHitTime, head.getTime());
		float accuracyTail = accuracyAssessingSystem.getAccuracy(endHitTime, tail.getTime());
		
		Log.i("GameSystem", "accuracy++:"+accuracyHead);  
		Log.i("GameSystem", "accuracy++:"+accuracyTail); 
		
		if(accuracyHead*accuracyTail < 0){
			scoringSystem.InvalidAction();
		}
		else {
			
			if(accuracyHead+accuracyTail < 0){
				scoringSystem.InvalidAction();
			}
			else {
				scoringSystem.addValidHit(accuracyHead);
				scoringSystem.addValidHit(accuracyHead);
				
				currentLyricsList.remove(head);
				currentLyricsList.remove(tail);
				result.setSuccess(true);
				result.setLevel(getLevel((accuracyHead+accuracyTail)/2));
				result.setWhichPillar(tail.getPoint());
			}
			
		}
	}
	private ArrayList<LyricsActionVO> getActiveLyrics(){
		ArrayList<LyricsActionVO> tempList = new ArrayList<LyricsActionVO>();
		
		if(judgeIndex > lyricsList.size()-1) return tempList;
		LyricsActionVO tempLyrics = lyricsList.get(judgeIndex);
		LyricsActionVO nextTempLyrics = null;
		
		
		if(tempLyrics.getType() == LyricsActionVO.TAP) {
			tempList.add(tempLyrics);		
			
			
			if(judgeIndex < lyricsList.size()-1)
				 nextTempLyrics = lyricsList.get(judgeIndex+1);
			
			
			
			if(		nextTempLyrics != null &&
					nextTempLyrics.getType() == LyricsActionVO.TAP && 
					(nextTempLyrics.getTime()-tempLyrics.getTime())<500 ){
				tempList.add(nextTempLyrics);	
				judgeIndex++;
			}
				
				
		}
		else {
			while(tempLyrics.getConnectionType() != LyricsActionVO.END_POINT){
				tempList.add(tempLyrics);
				judgeIndex++;
				tempLyrics=lyricsList.get(judgeIndex);
			}
			tempList.add(tempLyrics);
			
			if(judgeIndex < lyricsList.size()-1)
				 nextTempLyrics = lyricsList.get(judgeIndex+1);
			
			if(nextTempLyrics != null &&
					nextTempLyrics.getType() == LyricsActionVO.DRAG && 
					(nextTempLyrics.getTime()-tempLyrics.getTime())<500 ){
				tempList.add(nextTempLyrics);	
				judgeIndex = judgeIndex + 2;
				nextTempLyrics = lyricsList.get(judgeIndex);
				tempList.add(nextTempLyrics);
			}
		}

		judgeIndex++;
		
		return tempList;
	}
	public static GameSystem getInstance(){
		return gameSystem == null ? gameSystem = new GameSystem() : gameSystem;
	}
	
	public MusicVO getPlayingMusic() {
		return playingMusic;
	}
	public void setPlayingMusic(MusicVO playingMusic) {
		this.playingMusic = playingMusic;
	}
	public LyricsVO getPlayingLyrics() {
		return playingLyrics;
	}
	public void setPlayingLyrics(LyricsVO playingLyrics) {
		this.playingLyrics = playingLyrics;
	}

	public boolean isReady() {
		return ready;
	}

	public void setReady(boolean ready) {
		this.ready = ready;
	}

	public ArrayList<LyricsActionVO> getNewLyrics(){
		
		ArrayList<LyricsActionVO> list=new ArrayList<LyricsActionVO>();
		
		if(playIndex >= lyricsList.size()) return null;
		
		LyricsActionVO temp=lyricsList.get(playIndex);
		
		if(temp.getType() == LyricsActionVO.TAP) {
			list.add(temp);			
		}
		else {
			while(temp.getConnectionType() != LyricsActionVO.END_POINT){
				list.add(temp);
				playIndex++;
				temp=lyricsList.get(playIndex);
			}
			list.add(temp);
		}

		playIndex++;
		
		return list;
	}
	
	public boolean checkOverdue(int moment) {
		// TODO Auto-generated method stub
		if(currentLyricsList.size() == 0) return false;
		
		boolean flag = false;
		ArrayList<LyricsActionVO> copyCurrentLyricsList = new ArrayList<LyricsActionVO>(currentLyricsList);
		int size = copyCurrentLyricsList.size();
		
		for(int i=0;i<size;i++){
			LyricsActionVO actionVO = copyCurrentLyricsList.get(i);
			if(moment>actionVO.getTime()+AccuracyAssessingSystem.getActiveTimeAfter()) {
				
				flag = true;
				if(actionVO.getType() == LyricsActionVO.DRAG && actionVO.getConnectionType() == LyricsActionVO.BEGIN_POINT){
					
					currentLyricsList.remove(actionVO);
					currentLyricsList.remove(copyCurrentLyricsList.get(i+1));
					i++;				
				}
				else {
					currentLyricsList.remove(actionVO);
				}
				
			}
		}
		
		
		return flag;
	}
	
	public int getScore(){
		return scoringSystem.getScore();
	}
	public int getCombo(){
		return scoringSystem.getCombo();
	}
	public int getMaxCombo(){
		return scoringSystem.getMaxCombo();
	}

	public void loadResource() {
		// TODO Auto-generated method stub
		ready = false;
		LyricsReader reader=new LyricsReader();
		Lyrics lyrics = new Lyrics(playingLyrics.getId(), playingLyrics.getArtist(), playingLyrics.getName(), playingLyrics.getFilePath(), playingLyrics.getSource());
		lyrics.setScore(playingLyrics.getScore());
		lyrics.setNoteNum(playingLyrics.getNoteNum());
		lyrics.setTotalTime(playingLyrics.getTotalTime());
		try {
			reader.read(lyrics);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
		ArrayList<LyricsAction> list=lyrics.getLyricsActions();
		Iterator<LyricsAction> it=list.iterator();
		while (it.hasNext()) {
			LyricsAction lyricsAction =  it.next();
			lyricsList.add(new LyricsActionVO(lyricsAction));
			
			Log.i("GameSystem", lyricsAction.toString());
			Log.i("GameSystem", "-----------------------");
		}
		
		
		ready = true;
	}

	public boolean isRunning() {
		// TODO Auto-generated method stub
		return running;
	}


	public void setRunning(boolean isRunning) {
		// TODO Auto-generated method stub
		running = isRunning;
	}

	
	
	private void init() {
		// TODO Auto-generated method stub
		playIndex=0;
		judgeIndex=0;
		scoringSystem=new ScoringSystem();
		accuracyAssessingSystem=new AccuracyAssessingSystem();
		lyricsList=new ArrayList<LyricsActionVO>();
		currentLyricsList = new ArrayList<LyricsActionVO>();
		pActionList = new ArrayList<PackagedGameAction>();
	}
	public void reset(){
		init();
	}

	private String getLevel(float accuracy){
		if(accuracy == 1.0) return "Perfect!";
		else if(accuracy > 0.5 && accuracy <1.0) return "Good!";
		else return "Bad>_<";
	}

	public float getAverageAccuracy() {
		// TODO Auto-generated method stub
		return scoringSystem.getAverageAccuracy();
	}

	public String getLevel() {
		// TODO Auto-generated method stub
		return scoringSystem.getLevel();
	}
	
}
